
// INCLUDES ********************************************************************

#include "Vector2D.h"

#include "TextureCache.h"

#include "Texture2D.h"

#include "Sprite.h"

#include <QuartzCore/QuartzCore.h>

#include <OpenGLES/ES1/gl.h>

#include <exception>



namespace
{
    const size_t NumVertices = 8;
}

// NAMESPACES ******************************************************************

using namespace Meteor;


#pragma mark -
#pragma mark Constructors/Destructor
// -----------------------------------------------------------------------------

Sprite::Sprite( const std::string& fileName)

: Drawable(), mBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA),
  mVertices( NumVertices)
{
    Initialize( fileName);
}

// -----------------------------------------------------------------------------

Sprite::Sprite( const std::string& fileName, 
                const uint32_t zIndex)

: Drawable( zIndex), mBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA),
  mVertices( NumVertices)
{
    Initialize( fileName);
}

// -----------------------------------------------------------------------------

Sprite::Sprite( const Sprite& sprite)

: Drawable( sprite), mBlendFunc( sprite.mBlendFunc),
  mVertices( sprite.mVertices)
{
    Initialize( sprite.FileName());
}

// -----------------------------------------------------------------------------

Sprite& Sprite::operator=( const Meteor::Sprite& rhs)
{
    if ( this != &rhs)
    {
        return *this;
    }
    
    Drawable::operator=( rhs);
    
    mBlendFunc = rhs.mBlendFunc;

    mVertices = rhs.mVertices;

    Initialize( rhs.FileName());

    return *this;
}

// -----------------------------------------------------------------------------

Sprite::~Sprite()
{
    TextureCache::Instance().RemoveObject( mTexture->Filename());
}


#pragma mark -
#pragma mark Initializing
// -----------------------------------------------------------------------------

void Sprite::Initialize( const std::string& fileName)
{
    mTexture = TextureCache::Instance().AddObject( fileName);

    if ( mTexture == 0)
    {
        Logger::DebugLog( "Sprite::Initialize - error creating texture");
        
        return;
    }
    
    mFileName = fileName;
    
    SetSize( mTexture->Width(), mTexture->Height());
}

#pragma mark -
#pragma mark Texture handling
// -----------------------------------------------------------------------------

Texture2D* const Sprite::Texture() const
{
    return mTexture;
}

// -----------------------------------------------------------------------------

void Sprite::SetTextureRect( const Rectangle& rect)
{
    mTexture->SetRect( rect);
    
    mVertices[0] = mVertices[4] = -rect.Width()/2;
    mVertices[1] = mVertices[3] = -rect.Height()/2;
    
    mVertices[2] = mVertices[6] = rect.Width()/2;
    mVertices[5] = mVertices[7] = rect.Height()/2;
}


#pragma mark -
#pragma mark Size
// -----------------------------------------------------------------------------

void Sprite::SetSize( const float width, const float height)
{
    const Vector2D newSize( width, height);
    
    SetSize( newSize);
}

// -----------------------------------------------------------------------------

void Sprite::SetSize( const Vector2D& size)
{
    if (  mSize == size)
    {
        return;
    }
    
    mVertices[0] = mVertices[4] = -size.X()/2;
    mVertices[1] = mVertices[3] = -size.Y()/2;
    
    mVertices[2] = mVertices[6] = size.X()/2;
    mVertices[5] = mVertices[7] = size.Y()/2;
    
    Drawable::SetSize( size);
    
    //! if anchorpoint has been set before, the translation values
    //! are too low, becouse old size was used, so take new size into account
    mTranslation.SetX( mTranslation.X() * ( size.X() / mSize.X()) );

    mTranslation.SetY( mTranslation.Y() * ( size.Y() / mSize.Y()) );
}


#pragma mark -
#pragma mark BlendFunc
// -----------------------------------------------------------------------------

void Sprite::SetBlendFunc( const GLenum src, const GLenum dest)
{
    BlendFunction newBlendFunc( src, dest);

    mBlendFunc = newBlendFunc;
}

// -----------------------------------------------------------------------------

void Sprite::SetBlendFunc( const BlendFunction& newBlendFunc)
{
    mBlendFunc = newBlendFunc;
}

// -----------------------------------------------------------------------------

void Sprite::SetGLParameter( const GLenum name, const GLenum value)
{
    mTexture->SetParameter( name, value);
}

#pragma mark -
#pragma mark BlendFunc
// -----------------------------------------------------------------------------

const std::string& Sprite::FileName() const
{
    return mFileName;
}


#pragma mark -
#pragma mark Update/draw 
// -----------------------------------------------------------------------------

void Sprite::Update( const float deltaTime)
{
    Drawable::Update( deltaTime);
}

// -----------------------------------------------------------------------------

void Sprite::Draw() const
{
    if ( !mTexture || mVisible == false )
    {
        return;
    }
    
    glPushMatrix();
    
    glEnable( GL_BLEND);
    
    glEnableClientState( GL_VERTEX_ARRAY);
    
    glEnableClientState( GL_TEXTURE_COORD_ARRAY);
    
    glBlendFunc( mBlendFunc.source, mBlendFunc.destination);
    
    glVertexPointer( 2, GL_FLOAT, 0, &mVertices[0]);
    
    //! call base class functionality
    Drawable::Draw();
    
    mTexture->Draw();
    
    glDisableClientState( GL_TEXTURE_COORD_ARRAY);
    
    glDisableClientState( GL_VERTEX_ARRAY);
    
    glDisable( GL_BLEND);
    
    glPopMatrix();
}


